import 'dart:async';
import 'dart:convert';
import 'dart:io';

import 'package:crypto/crypto.dart';
import 'package:dio/dio.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter_oss_aliyun/flutter_oss_aliyun.dart';
import 'package:kq_flutter_core_widget/utils/ex/kq_ex.dart';
import 'package:kq_flutter_core_widget/utils/oss/oss_auth.dart';
import 'package:path_provider/path_provider.dart';

import '../oss/oss_client.dart';

class OssResult {
  /// 文件名
  String fileName;

  /// 文件路径
  String filePath;

  /// 文件大小
  int fileSize;

  OssResult(
      {required this.fileName, required this.filePath, required this.fileSize});
}

/// OSS调用原生工具类
class OssNativeUtil {
  /// 明文初始化
  static Future init(
      {required String ossEndpoint,
      required String bucketName,
      required String accessKey,
      required String accessSecret,
      Dio? dio}) async {
    OSSClient.init(
        ossEndpoint: ossEndpoint,
        bucketName: bucketName,
        accessKey: accessKey,
        accessSecret: accessSecret,
        dio: dio);
  }

  /// STS鉴权模式初始化
  static Future initSTS(
      {required String ossEndpoint,
      required String bucketName,
      required FutureOr<OSSAuthSTS> Function() authGetter,
      Dio? dio}) async {
    OSSClient.initSTS(
        ossEndpoint: ossEndpoint,
        bucketName: bucketName,
        authGetter: authGetter,
        dio: dio);
  }

  /// 阿里云获取缓存文件，返回文件路径
  static Future<OssResult?> getOssCacheFile({
    required String ossKey,
    double? width,
    double? height,
    int? quality,
    BoxFit scaleType = BoxFit.none,
  }) async {
    var fileName = generateCacheKey(
      ossKey,
      width: width,
      height: height,
      quality: quality,
      scaleType: _mapScaleType(scaleType),
    );
    var temp = await getTemporaryDirectory();
    var file = File('${temp.path}/oss/$fileName');
    if (!file.existsSync()) {
      return null;
    }

    var stat = file.statSync();
    var path = file.path;
    var index = path.lastIndexOf('/');
    String name;
    if (index == -1) {
      name = path;
    } else {
      name = path.substring(index + 1);
    }
    return OssResult(fileName: name, filePath: file.path, fileSize: stat.size);
  }

  /// 阿里云下载图片，返回图片路径
  static Future<OssResult?> ossDownloadImage(
      {required String ossKey,
      double? width,
      double? height,
      int? quality,
      BoxFit scaleType = BoxFit.none,
      CancelToken? cancelToken}) async {
    var scale = _mapScaleType(scaleType);
    var xOssProcess =
        "image/resize,m_$scale,w_${(width?.toInt() ?? 4096)},h_${(height?.toInt() ?? 4096)}";
    if (quality != null && quality >= 0 && quality <= 100) {
      xOssProcess += "/quality,Q_$quality";
    }

    var temp = await getTemporaryDirectory();
    var fileName = generateCacheKey(ossKey,
        width: width, height: height, quality: quality, scaleType: scale);
    var filePath = '${temp.path}/oss/$fileName';

    Response response;
    try {
      response = await OSSClient().downloadObject(
        ossKey,
        filePath,
        parameters: {'x-oss-process': xOssProcess},
        cancelToken: cancelToken,
      );
    } catch (e) {
      return null;
    }

    if (response.statusCode != 200) {
      return null;
    }

    var file = File(filePath);
    if (!file.existsSync()) {
      return null;
    }

    var stat = file.statSync();
    var path = file.path;
    var index = path.lastIndexOf('/');
    String name;
    if (index == -1) {
      name = path;
    } else {
      name = path.substring(index + 1);
    }
    return OssResult(fileName: name, filePath: file.path, fileSize: stat.size);
  }

  static String _mapScaleType(BoxFit boxFit) {
    if (boxFit == BoxFit.cover) {
      return 'mfit';
    } else if (boxFit == BoxFit.scaleDown || boxFit == BoxFit.contain) {
      return 'lfit';
    } else if (boxFit == BoxFit.fill) {
      return 'fixed';
    } else {
      return 'fill';
    }
  }

  /// 生产缓存图片的Key
  static String generateCacheKey(
    String ossKey, {
    double? width,
    double? height,
    int? quality,
    String? scaleType,
  }) {
    StringBuffer builder = StringBuffer();
    builder.write(ossKey);
    if (width != null) {
      builder.write('_W${width.toInt()}');
    }

    if (height != null) {
      builder.write('_H${height.toInt()}');
    }

    if (quality != null) {
      builder.write('_Q$quality');
    }

    if (scaleType.isNotNullOrEmpty) {
      builder.write('_M$scaleType');
    }

    var content = const Utf8Encoder().convert(builder.toString());
    var digest = md5.convert(content);
    return digest.toString();
  }

  /// 阿里云删除图片，返回是否成功
  static Future<bool> ossDelete({
    required String ossKey,
    CancelToken? cancelToken,
  }) async {
    try {
      var response =
          await OSSClient().deleteObject(ossKey, cancelToken: cancelToken);
      return response.statusCode == 204;
    } catch (e) {
      return false;
    }
  }

  /// 批量上传图片到OSS
  ///
  /// ossKeyAndPaths数据格式：[{'ossKey' : '',  'filePath': '', 'uploadOk': false}]。ossKey为文件上传到OSS的路径，
  /// filePath为本地需要上传的图片文件路径。uploadOk为false时执行上传，否则直接返回。ossKeyAndPaths对象属性除了ossKey,filePath
  /// 和uploadOk外，可以加自定义属性，最终会通过返回参数原封不动返回。
  ///
  /// 返回数据格式：[{'ossKey' : '',  'filePath': '', 'uploadOk': true, 'fileName': 'xx.png', 'fileSize': 1000}]。
  /// 存在上传失败的图片，则uploadOk为false。fileName和fileSize仅执行上传操作且上传成功的图片会返回此属性。
  /// 返回数据数量与[ossKeyAndPaths]保持一致。
  static Future<List<Map<String, dynamic>>?> uploadPics(
      List<Map<String, dynamic>> ossKeyAndPaths,
      {CancelToken? cancelToken}) async {
    if (ossKeyAndPaths.isEmpty) {
      return null;
    }

    for (var value in ossKeyAndPaths) {
      try {
        if (value['uploadOk'] == true) {
          continue;
        }

        var filePath = value['filePath'] as String?;
        // 路径不存在当做上传成功
        if (filePath == null || filePath.isEmpty) {
          value['uploadOk'] = true;
          continue;
        }

        var file = File(filePath);
        if (!file.existsSync()) {
          value['uploadOk'] = false;
          continue;
        }

        var fileName = value['fileName'] as String?;
        if (fileName.isNullOrEmpty && filePath.contains("/")) {
          value['fileName'] = filePath.substring(filePath.lastIndexOf("/") + 1);
        }

        var response = await OSSClient().putObjectFile(filePath,
            fileKey: value['ossKey'], cancelToken: cancelToken);

        if (response.statusCode == 200) {
          value['uploadOk'] = true;
          var stat = file.statSync();
          value['fileSize'] = stat.size;
        } else {
          value['uploadOk'] = false;
        }
      } catch (e) {
        value['uploadOk'] = false;
      }
    }
    return ossKeyAndPaths;
  }

  /// 上传单个文件到OSS
  ///
  /// [ossKey]为文件保存到OSS地址，[filePath]为本地需要上传文件地址，[mime]为本地上传文件类型
  static Future<bool> uploadSingleFile(String ossKey, String filePath,
      {String? mime,
      ProgressCallback? uploadProgress,
      CancelToken? cancelToken}) async {
    try {
      var response = await OSSClient().putObjectFile(
        filePath,
        fileKey: ossKey,
        cancelToken: cancelToken,
        option: PutRequestOption(
          onSendProgress: uploadProgress,
          headers: mime == null || mime.isEmpty ? null : {'content-type': mime},
        ),
      );
      return response.statusCode == 200;
    } catch (e) {
      return false;
    }
  }
}
